<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue3响应式原理可视化</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        body {
            background: linear-gradient(135deg, #0f2027, #203a43, #2c5364);
            color: #fff;
            min-height: 100vh;
            padding: 20px;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
        }
        header {
            text-align: center;
            margin-bottom: 30px;
            padding: 20px;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 15px;
            box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
            backdrop-filter: blur(5px);
        }
        h1 {
            font-size: 2.5rem;
            margin-bottom: 10px;
            color: #42b883;
        }
        .subtitle {
            color: #a9e34b;
            font-size: 1.2rem;
        }
        .content {
            display: flex;
            gap: 20px;
            flex-wrap: wrap;
        }
        .panel {
            flex: 1;
            min-width: 300px;
            background: rgba(40, 44, 52, 0.8);
            border-radius: 15px;
            padding: 20px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
        }
        .panel-title {
            font-size: 1.5rem;
            margin-bottom: 20px;
            color: #e9c46a;
            text-align: center;
            padding-bottom: 10px;
            border-bottom: 2px solid #42b883;
        }
        .reactive-data {
            margin-bottom: 30px;
        }
        .data-item {
            background: rgba(255, 255, 255, 0.1);
            padding: 15px;
            border-radius: 10px;
            margin-bottom: 15px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .data-value {
            font-weight: bold;
            color: #f4a261;
        }
        .effect-item {
            background: rgba(66, 184, 131, 0.2);
            padding: 15px;
            border-radius: 10px;
            margin-bottom: 15px;
            border-left: 4px solid #42b883;
        }
        .code {
            font-family: 'Fira Code', monospace;
            background: #2d3748;
            padding: 15px;
            border-radius: 10px;
            margin: 15px 0;
            overflow-x: auto;
            line-height: 1.5;
        }
        .code-line {
            margin-bottom: 8px;
        }
        .highlight {
            background: rgba(244, 162, 97, 0.2);
            padding: 2px 4px;
            border-radius: 4px;
            color: #f4a261;
        }
        .process {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 30px;
        }
        .process-step {
            width: 100%;
            padding: 20px;
            background: rgba(255, 255, 255, 0.05);
            border-radius: 10px;
            text-align: center;
            transition: all 0.3s ease;
        }
        .active {
            background: rgba(66, 184, 131, 0.3);
            transform: scale(1.02);
            box-shadow: 0 0 15px rgba(66, 184, 131, 0.5);
        }
        .step-title {
            font-size: 1.2rem;
            margin-bottom: 10px;
            color: #e76f51;
        }
        .arrow {
            font-size: 2rem;
            color: #42b883;
        }
        .controls {
            display: flex;
            justify-content: center;
            gap: 15px;
            margin-top: 30px;
        }
        button {
            padding: 12px 25px;
            border: none;
            border-radius: 8px;
            background: #42b883;
            color: white;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        button:hover {
            background: #359f72;
            transform: translateY(-2px);
        }
        button:active {
            transform: translateY(0);
        }
        .console {
            background: #1a202c;
            border-radius: 10px;
            padding: 15px;
            height: 200px;
            overflow-y: auto;
            font-family: 'Fira Code', monospace;
        }
        .log {
            margin-bottom: 8px;
            color: #a0aec0;
        }
        .log-time {
            color: #4299e1;
        }
        .log-event {
            color: #48bb78;
        }
        .log-detail {
            color: #ecc94b;
        }
        .dependency {
            display: flex;
            align-items: center;
            gap: 10px;
            margin-bottom: 15px;
            padding: 10px;
            background: rgba(255, 255, 255, 0.05);
            border-radius: 8px;
        }
        .dep-icon {
            width: 30px;
            height: 30px;
            border-radius: 50%;
            background: #42b883;
            display: flex;
            justify-content: center;
            align-items: center;
            font-weight: bold;
        }
        .track-line {
            stroke: #42b883;
            stroke-width: 2;
            stroke-dasharray: 5;
            animation: dash 1s linear infinite;
        }
        @keyframes dash {
            to {
                stroke-dashoffset: 10;
            }
        }
        .tracking {
            animation: pulse 1.5s infinite;
        }
        @keyframes pulse {
            0% { transform: scale(1); }
            50% { transform: scale(1.05); }
            100% { transform: scale(1); }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>Vue3响应式原理可视化</h1>
            <p class="subtitle">Track（依赖收集）与 Trigger（触发更新）过程演示</p>
        </header>

        <div class="content">
            <div class="panel">
                <h2 class="panel-title">响应式数据与Effect</h2>
                
                <div class="reactive-data">
                    <h3>响应式数据</h3>
                    <div class="data-item">
                        <span>count:</span>
                        <span class="data-value" id="count-value">0</span>
                    </div>
                    <div class="data-item">
                        <span>message:</span>
                        <span class="data-value" id="message-value">Hello Vue3</span>
                    </div>
                </div>

                <div class="effects">
                    <h3>Effect函数</h3>
                    <div class="effect-item">
                        <p>effect1: 依赖于count</p>
                        <div class="code">
                            <div class="code-line">() => {</div>
                            <div class="code-line">  <span class="highlight">console.log</span>("count is: ", <span class="highlight">count</span>);</div>
                            <div class="code-line">  document.getElementById("effect1").innerText = count;</div>
                            <div class="code-line">}</div>
                        </div>
                        <p>输出: <span id="effect1">0</span></p>
                    </div>
                    <div class="effect-item">
                        <p>effect2: 依赖于message</p>
                        <div class="code">
                            <div class="code-line">() => {</div>
                            <div class="code-line">  <span class="highlight">console.log</span>("message is: ", <span class="highlight">message</span>);</div>
                            <div class="code-line">  document.getElementById("effect2").innerText = message;</div>
                            <div class="code-line">}</div>
                        </div>
                        <p>输出: <span id="effect2">Hello Vue3</span></p>
                    </div>
                </div>
            </div>

            <div class="panel">
                <h2 class="panel-title">Track与Trigger过程</h2>
                
                <div class="process">
                    <div class="process-step" id="step1">
                        <div class="step-title">读取响应式数据</div>
                        <p>effect函数执行时读取响应式数据属性</p>
                    </div>

                    <div class="arrow">↓</div>

                    <div class="process-step" id="step2">
                        <div class="step-title">Track（依赖收集）</div>
                        <p>Proxy拦截get操作，调用track函数</p>
                        <p>将当前effect函数收集为依赖</p>
                        <div class="dependency">
                            <div class="dep-icon">1</div>
                            <div>effect1 → count</div>
                        </div>
                        <div class="dependency">
                            <div class="dep-icon">2</div>
                            <div>effect2 → message</div>
                        </div>
                    </div>

                    <div class="arrow">↓</div>

                    <div class="process-step" id="step3">
                        <div class="step-title">修改响应式数据</div>
                        <p>对响应式数据属性进行赋值操作</p>
                    </div>

                    <div class="arrow">↓</div>

                    <div class="process-step" id="step4">
                        <div class="step-title">Trigger（触发更新）</div>
                        <p>Proxy拦截set操作，调用trigger函数</p>
                        <p>找到对应的依赖effect函数并执行</p>
                    </div>
                </div>

                <div class="controls">
                    <button id="run-btn">执行Effect</button>
                    <button id="update-btn">更新数据</button>
                    <button id="reset-btn">重置</button>
                </div>
            </div>

            <div class="panel">
                <h2 class="panel-title">执行日志</h2>
                
                <div class="console" id="console">
                    <div class="log">等待操作...</div>
                </div>

                <h3>依赖关系图</h3>
                <div class="code">
                    <div class="code-line">// targetMap: WeakMap</div>
                    <div class="code-line">//   │</div>
                    <div class="code-line">//   └── target: Object</div>
                    <div class="code-line">//        │</div>
                    <div class="code-line">//        └── depsMap: Map</div>
                    <div class="code-line">//             │</div>
                    <div class="code-line">//             ├── key: "count" → dep: Set[effect1]</div>
                    <div class="code-line">//             └── key: "message" → dep: Set[effect2]</div>
                </div>
            </div>
        </div>
    </div>

    <script>
        // 模拟响应式系统
        const consoleEl = document.getElementById('console');
        let step1 = document.getElementById('step1');
        let step2 = document.getElementById('step2');
        let step3 = document.getElementById('step3');
        let step4 = document.getElementById('step4');
        let runBtn = document.getElementById('run-btn');
        let updateBtn = document.getElementById('update-btn');
        let resetBtn = document.getElementById('reset-btn');

        // 日志函数
        function log(message, type = 'info') {
            const time = new Date().toLocaleTimeString();
            const logEl = document.createElement('div');
            logEl.className = 'log';
            
            let html = `<span class="log-time">[${time}]</span> `;
            if (type === 'event') {
                html += `<span class="log-event">${message}</span>`;
            } else if (type === 'detail') {
                html += `<span class="log-detail">${message}</span>`;
            } else {
                html += message;
            }
            
            logEl.innerHTML = html;
            consoleEl.appendChild(logEl);
            consoleEl.scrollTop = consoleEl.scrollHeight;
        }

        // 模拟响应式数据
        let count = 0;
        let message = "Hello Vue3";

        // 模拟effect函数
        function effect1() {
            log("effect1执行: 读取count", 'event');
            document.getElementById('effect1').textContent = count;
            log(`count is: ${count}`, 'detail');
        }

        function effect2() {
            log("effect2执行: 读取message", 'event');
            document.getElementById('effect2').textContent = message;
            log(`message is: ${message}`, 'detail');
        }

        // 高亮显示步骤
        function highlightStep(step) {
            [step1, step2, step3, step4].forEach(s => s.classList.remove('active', 'tracking'));
            step.classList.add('active');
            
            if (step === step2) {
                step.classList.add('tracking');
            }
        }

        // 执行effect函数
        runBtn.addEventListener('click', () => {
            log("开始执行effect函数...", 'event');
            highlightStep(step1);
            
            setTimeout(() => {
                highlightStep(step2);
                log("track: 收集effect1对count的依赖", 'event');
                log("track: 收集effect2对message的依赖", 'event');
                
                setTimeout(() => {
                    effect1();
                    effect2();
                    log("effect函数执行完成", 'event');
                }, 1000);
            }, 1000);
        });

        // 更新数据
        updateBtn.addEventListener('click', () => {
            log("开始更新响应式数据...", 'event');
            highlightStep(step3);
            
            setTimeout(() => {
                highlightStep(step4);
                log("trigger: 找到count的依赖effect函数", 'event');
                
                setTimeout(() => {
                    count++;
                    document.getElementById('count-value').textContent = count;
                    log(`count更新为: ${count}`, 'detail');
                    
                    setTimeout(() => {
                        effect1();
                        log("effect1重新执行完成", 'event');
                    }, 800);
                }, 1000);
            }, 1000);
        });

        // 重置
        resetBtn.addEventListener('click', () => {
            count = 0;
            message = "Hello Vue3";
            document.getElementById('count-value').textContent = count;
            document.getElementById('message-value').textContent = message;
            document.getElementById('effect1').textContent = '';
            document.getElementById('effect2').textContent = '';
            
            consoleEl.innerHTML = '<div class="log">等待操作...</div>';
            [step1, step2, step3, step4].forEach(s => s.classList.remove('active', 'tracking'));
            
            log("已重置所有状态", 'event');
        });
    </script>
</body>
</html>